import './styles/index.css'
import components from './components'
import Expression from './expression'
import { createElement, toObject } from './utils'

const defaultOptions = {
  collapsed: false,
  toggler: false,
  layers: undefined,
  baseId: 'build',
  collapsedIcon: true,
}

// 支持的 渲染样式/布局
const supportPaintOrLayout: any = new Set([
  'circle-color', // 点颜色
  'circle-radius', // 点半径
  'line-color', // 线颜色
  'line-width', // 线宽
  'fill-color', // 面颜色
])

export default class LegendControl {
  _map: any
  _container: any
  _options: any
  _class: any
  _filterLayers: string[]

  /**
   * 构造函数
   * @param options
   * @param filterLayers
   */
  constructor(options: any, filterLayers: string[] = []) {
    this._class = 'mapboxgl-ctrl-legend'
    this._options = { ...defaultOptions, ...options }
    this._filterLayers = filterLayers
    this._loadLayers = this._loadLayers.bind(this)
  }

  /**
   * 添加控件
   * @param map
   */
  public onAdd(map: any) {
    this._map = map
    this._container = createElement('div', {
      classes: ['mapboxgl-ctrl', this._class],
    })
    this._map.on('styledata', this._loadLayers)
    return this._container
  }

  /**
   * 移除
   */
  public onRemove() {
    // this._container.parentNode.removeChild(this._container)
    this._container.remove()
    this._map.off('styledata', this._loadLayers)
    this._map = undefined
  }

  /**
   * 更新图例
   */
  public updateLegend() {
    this._loadLayers()
  }

  /**
   * 加载图层
   * @private
   */
  private _loadLayers() {
    const { collapsed, toggler, layers, baseId, collapsedIcon } = this._options
    const visibleLayers = toObject(layers)
    const showLegendLayers: any[] = this._map
      .getStyle()
      .layers.filter((layerItem: any) => {
        return (
          layerItem.source &&
          layerItem.source !== 'composite' &&
          !this._filterLayers.some((item: string) =>
            layerItem.id.includes(item)
          ) && // 过滤掉 base label 等图层
          layerItem.source !== 'gaode' &&
          (!layers ||
            Object.keys(visibleLayers).some((key) =>
              layerItem.id.match(key)
            )) &&
          this._map.getLayoutProperty(layerItem.id, 'visibility') === 'visible'
        )
      })
      .reverse() // 按地图上绘制的顺序显示(第一层在下面，最后一层在上面)
    const showLegendLayersClassName: string[] = []
    showLegendLayers.forEach((layer: any) => {
      const { id, layout, paint, metadata } = layer
      const selector: any = `${this._class}-pane-${baseId}-${id}`
      showLegendLayersClassName.push(selector)
      const previousPane: any = document.querySelector(`.${selector}`)
      // 单个图层的图例
      const pane = createElement('div', {
        classes: [selector, 'layer-legend-item', `${this._class}-pane`],
        attributes: {
          style: collapsed ? 'height: 32px;' : 'height: auto;',
        },
        content: [
          // 图层图例面板 Panel header
          createElement('div', {
            classes: ['layer-legend-item-title'],
            content: [
              this._createLegendTitleText(metadata?.name || id, selector), // Layer name or identifier
              collapsedIcon
                ? this._toggleCollapsedButton(id, selector)
                : undefined, // Toggler button
              toggler ? this._toggleButton(id) : undefined, // Toggler button
            ],
          }),
          // 图层图例内容 Panel content （过滤支持的图例）
          ...Object.entries({ ...layout, ...paint })
            .filter(([attribute]) => {
              if (!layers) {
                return supportPaintOrLayout.has(attribute)
              }
              const match: any = Object.keys(visibleLayers).find((key) =>
                id.match(key)
              )
              return (
                visibleLayers[match] === true ||
                visibleLayers[match]?.includes(attribute)
              )
            })
            .map(([attribute, expression]) => {
              const property = attribute.split('-').slice(-1)
              const parsed = Expression.parse(expression)
              return (
                // @ts-ignore
                parsed && components[property]?.(parsed, layer, this._map)
              )
            }),
        ],
      })
      // 已存在的替换
      if (previousPane) {
        this._container.replaceChild(pane, previousPane)
        // 添加
      } else {
        this._container.append(pane)
      }
    })
    this.checkDom(showLegendLayersClassName)
  }

  /**
   *  check 图层图例
   * @param showLegendLayersClassName
   * @private
   */
  private checkDom(showLegendLayersClassName: string[]) {
    const childrenLength: number = this._container.children.length
    for (let i = childrenLength - 1; i >= 0; i -= 1) {
      const childrenClassWithLayerId: string = this._container.children[
        i
      ].className.split(' ')[0]
      if (!showLegendLayersClassName.includes(childrenClassWithLayerId)) {
        this._container.children[i].remove()
      }
    }
  }

  /**
   * 单个图层图例标题文本
   * @param titleText
   * @param selector
   * @private
   */
  private _createLegendTitleText(titleText: string, selector: string) {
    return createElement('div', {
      classes: ['legend-item-title-text'],
      content: titleText,
      attributes: {
        title: titleText,
        onclick: (event: any) => {
          event.preventDefault()
          const previousPane: any = document.querySelector(`.${selector}`)
          previousPane.style.height =
            previousPane.style.height === '32px' ? 'auto' : '32px'
        },
      },
    })
  }

  /**
   * 切换按钮 (图层的显示隐藏)
   * @param layerId
   * @private
   */
  private _toggleButton(layerId: any) {
    const visibility =
      this._map.getLayoutProperty(layerId, 'visibility') || 'visible'
    return createElement('div', {
      classes: ['toggler', `toggler--${visibility}`],
      attributes: {
        onclick: (event: any) => {
          event.preventDefault()
          const visible = visibility === 'none' ? 'visible' : 'none'
          this._map.setLayoutProperty(layerId, 'visibility', visible)
        },
      },
    })
  }

  /**
   * 点击折叠
   * @param layerId
   * @param selector
   * @private
   */
  private _toggleCollapsedButton(layerId: any, selector: string) {
    return createElement('div', {
      classes: ['toggler-collapsed'],
      attributes: {
        onclick: (event: any) => {
          event.preventDefault()
          const previousPane: any = document.querySelector(`.${selector}`)
          previousPane.style.height =
            previousPane.style.height === '32px' ? 'auto' : '32px'
        },
      },
      content: [
        createElement('div', {
          classes: ['toggler-collapsed-arrow'],
        }),
      ],
    })
  }
}
